package com.markhsiu.common.memory;

/**
 * 一组数据块
 * 
 * @author Mark Hsiu
 *
 */
public class MMChunk {

	public static final int NUM_MAX = 1024;
	public static final int LENGTH_MAX = NUM_MAX * MMBlock.LENGTH_MAX;// 1024K
	private int size;// block个数
	private int remain;// 剩余block个数
	private MMBlock[] used;
	private MMBlock[] usable;
	private int usedIndex = 0;// 0..n
	private int usableIndex;// 0..n
	private MemoryPool pool;

	public MMChunk builder(MemoryPool pool, int offset, int length) {
		if (length > LENGTH_MAX || length < 0) {
			throw new IllegalArgumentException();
		}
		this.pool = pool;

		int num = length / MMBlock.LENGTH_MAX;
		used = new MMBlock[num];
		usable = new MMBlock[num];

		int newOffset = offset;
		for (int i = 0; i < num; i++) {
			usable[i] = new MMBlock().builder(this, newOffset);
			newOffset += MMBlock.LENGTH_MAX;
		}

		size = num;
		remain = num;
		usableIndex = num;
		return this;
	}

	public MMBlock[] alloc(int num) {
		if (num > remain || remain == 0) {
			throw new IllegalArgumentException("chunk内存可用block不足");
		}
 
		int index = usableIndex - num;
		MMBlock[] newBlocks = new MMBlock[num];
		System.arraycopy(usable, index, newBlocks, 0, num);
		System.arraycopy(newBlocks, 0, used, usedIndex, num); 
		
		for (MMBlock block : newBlocks) {
			if (block.used()) {
				throw new IllegalArgumentException("chunk内存块分配错误,存在block已经分配使用 : " + block);
			}
			block.setChunkIndex(++usedIndex);
		}
		remain -= num;
		usableIndex = index;
		
		return newBlocks;
	}

	void free(MMBlock block) {
		if (block == null || !block.used()) {
			throw new IllegalArgumentException("释放内存block错误，当前block未分配 ： " + block);
		}
		int index = block.chunkIndex();
		
		usable[usableIndex] = block.setChunkIndex(0);
		if(usedIndex != index){
			used[index - 1] = used[usedIndex - 1].setChunkIndex(index);
		}
		usableIndex ++;
		usedIndex --;
		remain ++;
		 
		pool.reStore(this, MMBlock.LENGTH_MAX);
	}

	public boolean isEmpty() {
		return remain == 0;
	}

	public int size() {
		return size;
	}

	public int remain() {
		return remain;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("{ccode:");
		sb.append(hashCode());
		sb.append("},{usedIndex:");
		sb.append(usedIndex);
		sb.append("},{usableIndex:");
		sb.append(usableIndex);
		sb.append("}");
		return sb.toString();
	}
}
